package com.wjbgn.bsolver.gateway.filter;


import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.utils.StringUtils;
import com.wjbgn.bsolver.gateway.client.dto.UserDTO;
import com.wjbgn.bsolver.gateway.enums.CommonReturnEnum;
import com.wjbgn.bsolver.gateway.util.dto.ErrorResult;
import com.wjbgn.bsolver.gateway.util.dto.Result;
import com.wjbgn.bsolver.gateway.util.jwt.JwtUtil;
import com.wjbgn.bsolver.gateway.util.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 权限验证过滤器
 *
 * @author weirx
 * @date 2022/03/14 10:25
 **/
@RefreshScope
@Component
public class AuthFilter implements GlobalFilter {

    @Value("#{'${gateway.filter.white-list}'.split(',')}")
    private List<String> whiteList;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders header = request.getHeaders();
        String token = header.getFirst(JwtUtil.HEADER_AUTH);
        String username = header.getFirst(JwtUtil.HEADER_USERNAME);
        String murmur = header.getFirst("murmur");
        PathContainer pathContainer = request.getPath().pathWithinApplication();
        String path = pathContainer.value();

        //白名单，放行
        if (whiteList.contains(path)) {
            return chain.filter(exchange);
        }
        //token为空
        if (StringUtils.isEmpty(token)) {
            return this.sendError(path, exchange.getResponse(), CommonReturnEnum.UNAUTHORIZED.getCode(),
                    CommonReturnEnum.UNAUTHORIZED.getName());
        }
        //验证token
        String tokenCache = redisUtil.getString(JwtUtil.REDIS_TOKEN_PREFIX + username);
        if (StringUtils.isEmpty(tokenCache)) {
            Map<String, String> stringStringMap = JwtUtil.validateToken(token);
            if (ObjectUtil.isEmpty(stringStringMap)) {
                return this.sendError(path, exchange.getResponse(), CommonReturnEnum.UNAUTHORIZED.getCode(),
                        CommonReturnEnum.UNAUTHORIZED.getName());
            }
        }
        //没过期，对比当前token和缓存的token是否一致
        if (!token.equals(tokenCache)) {
            boolean tokenAndUser = JwtUtil.validateTokenAndUser(token, username);
            if (!tokenAndUser) {
                return this.sendError(path, exchange.getResponse(), CommonReturnEnum.UNAUTHORIZED.getCode(),
                        CommonReturnEnum.UNAUTHORIZED.getName());
            }
        }
        //token验证通过，token续期
        String refreshToken = JwtUtil.refreshToken(token, username);
        redisUtil.setObjectExpire(JwtUtil.REDIS_TOKEN_PREFIX + username, refreshToken,
                JwtUtil.REDIS_TOKEN_EXPIRE_MINUTE, TimeUnit.MINUTES);
        redisUtil.publish("MESSAGE_" + murmur, Result.success(new UserDTO(username, refreshToken)));

        //将用户信息存放在请求header中传递给下游业务
        ServerHttpRequest.Builder mutate = request.mutate();
        mutate.header(JwtUtil.HEADER_USERNAME, username);
        ServerHttpRequest buildRequest = mutate.build();
        return chain.filter(exchange.mutate().request(buildRequest).build());
    }

    protected Mono<Void> sendError(String url, ServerHttpResponse httpResponse, int code, String msg) {
        httpResponse.setStatusCode(HttpStatus.OK);
        httpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        ErrorResult result = new ErrorResult();
        result.setCode(code);
        result.setMessage(msg);
        result.setPath(url);
        result.setSuccess(false);
        result.setTimestamp(new Date());
        byte[] responseByte = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = httpResponse.bufferFactory().wrap(responseByte);
        return httpResponse.writeWith(Flux.just(buffer));
    }
}
